package cn.itsource.basic.aop;

import cn.hutool.extra.spring.SpringUtil;
import cn.itsource.util.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect     //我是一个AOP切面类
@Component  //将类交给spring容器管理
@Slf4j
public class CacheAOP {

    //公式 = 切入点表达式 + 通知方法
    @Pointcut("@annotation(myCache)") //方法参数级别
    public void pointCut(MyCache myCache){
    }



    /**
     * 需求:获取目标对象的相关信息.
     *      1.获取目标方法的路径    包名.类名.方法名
     *      2.获取目标方法的类型  class
     *      3.获取传递的参数
     *      4.记录当前的执行时间
     */
    @Before("pointCut(myCache)")
    //@Before("bean(itemCatServiceImpl)")
    public void before(JoinPoint joinPoint,MyCache myCache){
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        Class targetClass = joinPoint.getTarget().getClass();
        Object[] args = joinPoint.getArgs();
        Long runTime = System.currentTimeMillis();
        log.info("方法路径:" +className+"."+methodName);
        log.info("目标对象类型:" + targetClass);
        log.info("参数:" + Arrays.toString(args));
        log.info("执行时间:" + runTime+"毫秒");
    }

//    @AfterReturning("pointCut()")
//    public void afterReturn(){
//        log.info("我是后置通知");
//    }
//
//    @After("pointCut()")
//    public void after(){
//        log.info("我是最终通知");
//    }

    /**
     * 环绕通知说明
     * 注意事项:
     *  1.环绕通知中必须添加参数ProceedingJoinPoint
     *  2.ProceedingJoinPoint只能环绕通知使用
     *  3.ProceedingJoinPoint如果当做参数 则必须位于参数的第一位
     */
    @Around("pointCut(myCache)")

    public Object around(ProceedingJoinPoint joinPoint,MyCache myCache) {
        Object result = null;
        //通过注解获取当前key
        String key = myCache.key();
        //通过注解获取当前设置时间
        long minute = myCache.minute();
        //获取到 redis的 实现类
        RedisService redisService = SpringUtil.getBean(RedisService.class);
        //2. 查询redis 之后判断是否有数据
        //动态获取方法的返回值类型
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Class returnType = methodSignature.getReturnType();
        if (redisService.exists(key)) {
            //redis中有记录,无需执行目标方法
            result = redisService.getKeyObjectValue(key, returnType);
            log.info("AOP当前从redis获取缓存:数据为:{}",result);
        } else {
            //表示reids数据不存在,需要查询数据库
            try {
                result = joinPoint.proceed();  //执行目标方法及通知
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            //将查询的结果保存到redis中去
            //判断数据是否需要超时时间
            if (minute > 0) {
                redisService.setStringKeyAndValue(key, result, minute);
            } else {
                redisService.setStringKeyAndValue(key, result);
            }
            log.info("aop执行目标方法查询数据库");
        }

        return result;
    }

}
